//
//  WebServer.cpp
//  Phidgets
//
//  Created by Daniel Hazelbaker on 11/5/11.
//  Copyright 2011 Blue Box Moon. All rights reserved.
//

#include <iostream>
#include <list>
#include "WebServer.h"
#include "Utils.h"
#include "MonkeyError.h"
#include "json.h"


#define CALL_MEMBER_FN(object,ptrToMember)  ((object).*(ptrToMember))


//
// Initialize a new seb server with the specified document root for static content.
//
WebServer::WebServer(std::string document_root) : mongoose::MongooseServer()
{
    setOption("document_root", document_root.c_str());
    setOption("listening_ports", "8080");
    setOption("num_threads", "5");
}


//
// Handle a web request into the server.
//
bool WebServer::handleEvent(mongoose::ServerHandlingEvent eventCode, mongoose::MongooseConnection &connection, const mongoose::MongooseRequest &request, mongoose::MongooseResponse &response)
{
    std::string uri, header;
    WebServerHandlers::iterator handlerIt;
    WebHandler *handler = NULL;
    WebHandlerData data = WebHandlerData(connection, request, response);
    StringDictionary values;
    
    
    //
    // Remove trailing slash if there is one.
    //
    uri = request.getUri();
    if (uri.size() > 1 && *(--uri.end()) == '/')
        uri.erase(--uri.end());

    //
    // Make sure this is for a new request.
    //
    if (eventCode != MG_NEW_REQUEST)
        return false;

    //
    // Check if it is well known static content.
    //
    if (uri.compare(0, 4, "/js/") == 0 || uri.compare(0, 5, "/css/") == 0 || uri.compare(0, 8, "/images/") == 0)
        return false;

    //
    // Check if this is for a dynamic request.
    //
    for (handlerIt = _handlers.begin(); handlerIt != _handlers.end(); handlerIt++)
    {
        if (request.getRequestMethod() == (*handlerIt).method && MatchRequest(uri, (*handlerIt).uri, values))
        {
            handler = &(*handlerIt);
            break;
        }
    }

    //
    // If no handler found, it is for static content.
    //
    if (handler == NULL)
        return false;

    printf("Got %d request at '%s'\r\n", eventCode, uri.c_str());
    
    //
    // Set some defaults for this response, sub-handlers can override this.
    //
    response.setStatus(200);
    response.setConnectionAlive(false);
    response.setContentType("text/html");

    //
    // Call the appropriate sub-handler.
    //
    handler->handler(data, values);

    //
    // Send the response.
    //
    response.write();
    
    return true;
}


//
// Determine if the current URI matches the handler's URI. Also fills out the values
// dictionary with any values found in the handler URI.
//
bool WebServer::MatchRequest(const std::string &uri, const std::string &handlerUri, StringDictionary &values) const
{
    const char *cUri, *cHandlerUri, *co, *cc, *v;
    std::string key, value;
    
    
    //
    // Ensure values is empty when we start.
    //
    values = StringDictionary();

    cUri = uri.c_str();
    cHandlerUri = handlerUri.c_str();

    for (co = strchr(cHandlerUri, '{'); co != NULL; co = strchr(cHandlerUri, '{'))
    {
        //
        // Compare up to the { character.
        //
        if (strncmp(cHandlerUri, cUri, (co - cHandlerUri)) != 0)
            break;
        
        //
        // Move up to this point in the search.
        //
        cUri += (co - cHandlerUri);
        cHandlerUri = co;
        
        //
        // co points to the { character, look for a closing }. If no closing } then abort.
        //
        co++;
        cc = strchr(co, '}');
        if (cc == NULL)
            break;
        
        //
        // Pull the key name out, it's between the { and }.
        //
        key = std::string(co, (cc - co));
        cc++;

        //
        // Get and move past the value as a number.
        //
        for (v = cUri; isnumber(*v); v++)
            ;
        value = std::string(cUri, (v - cUri));
        cUri = v;

        //
        // Store they key/value pair in the dictionary.
        //
        values[key] = value;
        
        //
        // Move to the next point. cUri has already been updated.
        //
        cHandlerUri = cc;
    }
    
    //
    // If the search was aborted, then return false == no match.
    //
    if (co != NULL)
        return false;

    //
    // Verify the remaining string is the same.
    //
    return (strcmp(cUri, cHandlerUri) == 0);
}

//
// Retrieve the value from the POST/GET data for the given key.
//
const std::string WebServer::getRequestValue(const mongoose::MongooseRequest &request, std::string key)
{
    std::string var;

    
    request.getVar("code", var);
    
    return var;
}

//
// Register a new handler at the given URI. If a handler has already been registered
// then it is replaced.
//
void WebServer::RegisterHandler(std::string uri, std::string method, WebServerHandler handler)
{
    _handlers.push_back(WebHandler(method, uri, handler));
}


